home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Almathera Ten Pack 3: CDPD 3
/
Almathera Ten on Ten - Disc 3: CDPD3.iso
/
ab20
/
ab20_archive
/
datacomm
/
xpr
/
xprkermit-1.111.lzh
/
kermitproto.w
< prev
next >
Wrap
Text File
|
1991-12-21
|
62KB
|
2,089 lines
/*
* A completely new C Kermit module.
*
* Based on code from Frank Da Cruz's excellent book, _Kermit: A File
* Transfer Protocol_, Digital Press, 1986.
*
* As this code is almost entirely from said book, it is certainly covered
* by that book's copyright. Basically, this means the code is freely
* distributable, and can be used in a commercial program provided such
* use does not increase the program's cost to the purchaser beyond a small
* handling fee.
*
* Stephen Walton, swalton@solar.stanford.edu
* Dept. of Physics and Astronomy
* California State University, Northridge
* Northridge, CA 91330
*
* ORGANIZATION
*
* This file is in three pieces, and could probably be broken into
* two files. Leading off are #DEFINE's and declarations of
* global and external variables. Following are the unmodified sources
* from The Book. Then are the z* file-handling routines written using
* standard Unix-style (almost ANSI C) file routines.
*
* I make no apologies for the organization; my primary goals were (1) to
* use the unmodified book source except for errors found by Lint, and
* (2) to allow this file to be plugged into an otherwise unmodified
* terminal program for the Commodore Amiga computer called VT100. The
* comments starting with the string "lint" are for the Lint program
* of Gimpel Software, available for the Commodore Amiga and MS-DOS
* machines. I highly recommend it.
*
* A few words about style herein. Both "book Kermit" and the official
* C Kermit release make extensive use of global variables to set various
* options and keep track of what is going on. I don't like this, but
* since example source code must be part of the Kermit specification,
* I chose to keep that organization. There are several places where I
* I have tried to modularize things better. First of all, everything
* which is not needed outside of this file is declared "static". Second,
* I have kept the book Kermit code pretty much intact, except for some lint
* related things and one extra convention: tmsg() appends characters to
* an existing status line, but tmsgl() is required to force that output to
* be seen. Hence, I've changed the last in each series of tmsg() calls to
* tmsgl().
*/
/*
* Revision History--Versions 0 through 1 never left me.
*
* Version 0.5--Created and linted.
* Version 0.6--Added RESUME to handling of "unknown packets".
* --Added proto() function to wrap around wart() for handling
* of startup and cleanup functions.
* --Changed handling of timeouts in input(): instead of an
* error("Timeout") call, it puts the "Timeout" message in
* the data field and pretends it read an 'E' packet.
* Version 0.7--Fixed a problem with rpsiz and MAXRP. I had set rpsiz
* to MAXRP in rpar() and was calling ttinl() with a max
* argument of MAXRP, which resulted in truncated packets;
* to wit, MAXRP+1 characters could come into ttinl counting
* the eol. Created defines DRPSIZ/DSPSIZ for default values
* for rpsiz and spsiz, and increased MAXRP.
* --Changed rcvpkt, sndpkt, and data so that they are pointers
* instead of static arrays, and allocate and de-allocate
* them in proto().
* Version 0.8--Added client support. To make a Get
* command, point the char *cmarg at the remote file
* specification and set the start state to 'r'
* (extern char start in the calling file).
* Version 0.9--Added long packet support. This required adding an extern
* int urpsiz which is set to the desired receive packet size
* by the user interface.
* --Since the above required using the capas, I also added the
* code from C Kermit 4E(070) for negotiating window size
* in rpar() and spar(). Of course, we don't do windows yet.
* --Fixed a bug which is also part of C Kermit 4E(070).
* If we tell the sender that we can receive a packet
* of size MAXRP, the packet can contain as many as
* MAXRP+5 characters, counting the EOL and extended
* headers. So rpack() must be able to handle somewhat
* more characters than the actual maximum packet length.
* I defined MAXRP here to be 1024, but only allow
* rpsiz in proto() to be MAXRP-20.
* Version 0.95--Added code to gracefully abort transfer. This works
* via the cx and cz external variables and the state 'a'.
* Version 1.0--Unleashed upon the world.
* Version 1.1--Marco Papa (papa on BIX, papa@pollux.usc.edu on Internet)
* added the code between the #ifdef XPRKERMIT...#endif
* pairs, which turns this code into a routine which can
* be used as an Amiga external file transfer protocol.
* Upon getting it back from him, I fixed two lingering
* bugs: there was an "if (ebqflg & b8)" in encode()
* which should be &&, and the first argument of encode()
* needed to be declared as an int, not a char.
* --Allow user to set bctr (block check to request) himself.
* --If urpsiz is illegal, we set rpsiz to a legal value; I also
* set urpsiz to this as well, so the user knows what's up.
* Version 1.2--Added a typedef of CHAR to unsigned char to allow this code
* to work with the latest version of Wart (V 1A(006) dated
* 12 January 1989.
* Version 1.3--21 December 1989
* (1) Added a tflush() call to the end of decode(). This
* gives the user an opportunity to flush out the messages
* from X packets, if the I/O is buffered.
*
* Version 1.4-- 5 January 1989
* (1) Added the counters for characters on a per-file and
* per-transaction basis. Later, we can use the per-file
* ones and a timer to figure out how long we have to go
* on the current file.
* (2) Started replacing tchar() calls with calls to the
* screen() function, again a la C Kermit. #define's for
* the manifest constants for screen() are in kermitproto.h,
* copied from C Kermit.
*
* Version 1.6-- 18 August 1990 (Version 1.4 was released)
* (1) Added ANSI-specific items, in particular the #include
* of "kermitproto.h" and "xproto.h" which have all of the
* prototypes.
* (2) Fixed gnfile(), which was not being properly re-
* initialized if a send was canceled.
*
* Version 1.63--2 December 1990
* (1) Changed the behavior of the xpr chkint() function so
* that the error exit is taken if (*xpr_chkabort)() returns
* -1: an error packet is made up and sent and the protocol
* quits immediately.
* (2) Discovered that the file character count in gnchar()
* skipped nulls! Fixed.
*
* Version 1.64--5 December 1990
* Fixed zgetc(), which was hanging onto characters from the
* previous file transfer if it was cancelled.
*
* Version 1.67--14 December 1990 (includes 1.65 through 1.67 changes)
* Added buffered I/O from C Kermit 5A(163).
* Replaced getpkt() with version from C Kermit 5A. Got rid
* of encode(), since it is built in.
* Fixed a long standing block check 2 bug--a missing break
* in the case statement in rpack()!
*
* Version 1.72--18 December 1990
* Fixed *MAJOR* bugs in getpkt() from C Kermit: maxsize()
* is spurious, and should be bufmax; and the leftover[]
* array needs to be 7 characters long, not 6: the worst
* case is two successive eight-bit-quoted control chars.
*/
/*
* Run this through the Wart preprocessor which comes with C Kermit.
*/
#ifndef _lint
static char rcsid[] = "$Header: Work:src/xprkermit/RCS/kermitproto.w,v 1.3 91/11/09 06:49:18 swalton Exp Locker: swalton $";
#endif
#ifdef XPRKERMIT
extern long (*xupdate)(), (*xswrite)(), (*xfopen)(), (*xfclose)(), (*xfread)(),
(*xfwrite)(), (*xsread)(), (*xchkabort)(void), (*xffirst)(), (*xfnext)(),
(*xsflush)(void), (*xfinfo)(), (*xgets)(), (*xunlink)(), (*xsquery)(void),
(*xchkmisc)(void);
extern long brkflag;
#include "types.h"
#include "xprkermit.h"
#endif
#include "kermitproto.h"
#include <stdlib.h>
#include <string.h>
/* kermitproto.c */
static int input(void);
static void errpkt(char *s);
#pragma regcall(errpkt(a0))
static int nxtpkt(void);
static int tinit(void);
static int error(char *s);
#pragma regcall(error(a0))
static int ack(void);
static int ack1(char *s);
#pragma regcall(ack1(a0))
static int nak(void);
static int sinit(int c);
#pragma regcall(sinit(d0))
static int scmd(int t, char *s);
#pragma regcall(scmd(d0,a0))
static int rinit(void);
static void resetc(void);
static int sfile(void);
static int sdata(void);
static int seof(char *s);
#pragma regcall(seof(a0))
static int seot(void);
static int rcvfil(void);
static int reof(char *);
#pragma regcall(reof(a0))
static void fstats(void);
static int closof(int nokeep);
#pragma regcall(closof(d0))
static int spack(int type, int n, int len, CHAR *d);
#pragma regcall(spack(d0,d1,d2,a0))
static int resend(void);
static int rpack(void);
static unsigned chksum(CHAR *s);
#pragma regcall(chksum(a0))
static unsigned chk1(CHAR *s);
#pragma regcall(chk1(a0))
static unsigned chk3(CHAR *s);
#pragma regcall(chk3(a0))
static int getpkt(int maxlen);
#pragma regcall(getpkt(d0))
static int decode(void);
static int encstr(char *s);
#pragma regcall(encstr(a0))
static int decstr(char *s);
#pragma regcall(decstr(a0))
static int spar(CHAR *s);
#pragma regcall(spar(a0))
static char *rpar(void);
static int zclear(void);
static int zopeni(char *name);
#pragma regcall(zopeni(a0))
static int zopeno(char *name);
#pragma regcall(zopeno(a0))
static int zclosi(void);
static int zcloso(int discard);
#pragma regcall(zcloso(d0))
static int zrtol(char *s1, char *s2, int warn);
#pragma regcall(zrtol(a0,a1,d0))
static int zltor(char *s1, char *s2);
#pragma regcall(zltor(a0,a1))
static int zinfill(void);
static int zoutdump(void);
static int tflush(void);
/*
* Buffered I/O stuff stolen from real C Kermit
*/
#define INBUFSIZE 1024 /* Size of record buffer. */
#define OBUFSIZE 1024 /* Size of output buffer. */
#define MAXNAMELEN 256 /* Maximum file name length */
static CHAR *zinbuffer, *zoutbuffer;
static CHAR *zinptr, *zoutptr; /* Buffer pointers */
static int zincnt, zoutcnt; /* Buffer char counters */
/* get the next char; sorta like a getc() macro */
#define zminchar() (((--zincnt)>=0) ? ((int)(*zinptr++) & 0377) : zinfill())
/* stuff a character into the input buffer */
#define zmstuff(c) zinptr--, *zinptr = c, zincnt++
/* put a character to a file, like putchar() macro */
#define zmchout(c) \
((*zoutptr++=(CHAR)(c)),(((++zoutcnt)>=OBUFSIZE)?zoutdump():0))
/*
* Here are the variables which need to be set to startup values, and which
* also can be freely changed between protocol transfers. At first I thought
* to declare them all "extern" in order to force definition elsewhere.
* On reflection, it makes sense to both declare them here and set them to
* their default startup values. Thus they can be ignored outside of this
* module if you so desire.
*
* Note that the names are very systematic: Names beginning with "r" have
* to do with values I use for received packets; those beginning with "s"
* are values I use for sending packets. Also note we set some, others are
* set for us. I have made the ones we get in spar() static (local),
* and the ones we send in rpar() global.
*
* First the ints.
*/
int cx = 0,
cz = 0, /* Flags for aborting transfers. cx (control-X)*
* is set to 1 if an abort of the current file *
* is desired, cz (control-Z) if an abort of *
* an entire batch transfer is desired. */
rpsiz = MAXRP, /* Maximum packet size */
/* Like most of the receive packet parameters, *
* this one is actually set by the sender. But *
* since the sender has the option to not send *
* these, we must initialize to "reasonable" *
* defaults. */
bctr = 1, /* Block check type to request. */
limit = 5, /* Retry limit on receive */
warn = 0, /* 1 for warn before overwriting files */
keep = 0, /* 1 to keep incomplete files */
rpadn = 0, /* Number of pad characters I require. */
rtimo = 10; /* How long I want you to wait before you *
* you time me out. */
CHAR rmark = '\1', /* Start of packet marker for receive */
reol = '\r', /* End of packet marker for receive */
start = 0, /* Start state */
sctlq = '#', /* Control character quote character for send */
rpadc = '\0'; /* Pad character I want you to use */
/*
* Variables which MUST be set by the external interface.
*/
extern int
parity, /* 0 for no parity--need for proper 8th-bit quote */
text, /* Flag 1 for text file, 0 for binary file */
urpsiz; /* Maximum receive packet size user wants. */
extern char
*cmarg; /* Character string containing Kermit server cmd */
/*
* Variables having to do with the status of the file transfer.
*/
static long tfc, /* File chararacters sent/received, total. */
ffc, /* " " " , current file. */
timeouts, /* Number of timeouts */
tlci, /* Comm line characters in, total. */
flci, /* " " " ", current file. */
tlco, /* " " " ", total. */
flco; /* " " " ", current file. */
/*
* In a fit of cleverness, here are some macro defines for variables we *
* aren't currently using. Only now we tell Lint to ignore constant *
* Booleans!
*/
/*lint -e506 */
#define local 1 /* Local mode flag--that is, I'm on your end */
#define server 0 /* We are never server */
#define delay 0 /* Time to delay before sending first packet */
#define xpkt 0 /* Send X packet instead of F? */
/*
* This block of defines is strictly internal flags of various kinds. *
* I hope to Grid that I've got them all. Someday this will be cleaner *
*/
static int
spsiz = DSPSIZ, /* Maximum send packet size */
wsize = MAXWS, /* Maximum window size */
sndpkl, /* Size of packet currently being attempted */
filcnt, /* Number of files transferred so far */
bctu = 1, /* Block check type to use */
rqf, /* Flag for 8th bit quote negotiations */
ebqflg = 0, /* 8th-bit quoting flag */
xflag, /* Output to screen for generic server commands */
rq = 0, /* Received 8bq bid */
rpt = 0, /* Repeat count */
rptflg = 0, /* Repeat processing flag */
capas = 10, /* Final position of inbound capas mask */
atcapr = 0, /* Attribute capability requested */
atcapu = 0, /* Attribute capability used */
swcapr = 0, /* Sliding windows capability requested */
swcapu = 0, /* Sliding windows capability used */
lpcapr = 0, /* Long packets capability requested */
lpcapu = 0, /* Long packets capability used */
rsn, /* Received sequence number */
seq = 0, /* Current sequence number */
rln, /* Length of received data field */
size, /* Current size of output packet data */
first = 0, /* Some kind of lookahead flag */
stimo = 5, /* Timeout interval for me to use */
spadn = 0; /* Number of pad characters for me to use */
#define atcapb 8 /* Attribute capability bit */
#define swcapb 4 /* Sliding windows capability bit */
#define lpcapb 2 /* Long packets capability bit */
#define closif zclosi /* I use closif() to close the input file in *
* case it needs to be more complex later, but *
* for now it just calls the z routine. */
static char
filnam[MAXNAMELEN], /* Current file name */
myname[MAXNAMELEN], /* Current local file name */
ssc, /* Start server command */
memstr, /* Use in-memory string? */
*osp = NULL; /* Output string pointer */
static CHAR
smark = '\1', /* Start of packet marker for send */
spadc = '\0', /* Pad character to use on sending */
seol = '\r', /* End of packet marker for sending */
rctlq = '#', /* Control character quote char for receiving */
ebq = '&', /* 8th-bit prefix */
sq = 'Y', /* Sent 8bq bid */
rptq = '~', /* Repeat prefix */
*memptr = NULL, /* Pointer to characters in memory */
*sndpkt, /* Send packet. */
*rcvpkt, /* Receieve packet. */
*data, /* Data to send/receive before encode/decode */
*rdatap; /* Pointer to null-terminated data field */
#define ERR(s) error(s); RESUME
#define RESUME return
/*lint -save -e525 -e527 We don't care how Wart formats! */
/*lint -e716 -e744 -e570 -esym(745,wart) Other Wart peculiarities */
%states ssfil ssdat sseot
%states srini srfil srdat
%states sipkt srgen
%%
/* Start states */
s { /* - Start State - */
tinit(); /* Initialize transaction */
if (sinit('S') < 0) { ERR("sinit"); } /* Build, send Send-Init. */
else { /* If successful, */
filcnt = 0; /* initialize file counter */
BEGIN ssfil; /* and switch to ssfil state. */
}
}
v { tinit(); rinit(); BEGIN srini; } /* - Receive - */
r { /* Get */
tinit(); ssc = 0;
if (sinit('I') < 0) { ERR("sinit"); }
else BEGIN sipkt;
}
c { /* Host */
tinit(); ssc = 'C';
if (sinit('I') < 0) { ERR("sinit"); }
else BEGIN sipkt;
}
g { /* Generic */
tinit(); ssc = 'G';
if (sinit('I') < 0) { ERR("sinit"); }
else BEGIN sipkt;
}
a { /* Immediate protocol abort */
spack('E', seq, 21, (CHAR *) "User aborted protocol");
closif(); closof(1); /* Close files, deleting output */
RESUME;
}
/* Dynamic states, sending file(s) */
<ssfil>Y { /* - Send File State - */
if (filcnt++ == 0) spar(rdatap); /* Set parameters if 1st time */
cx = 0; /* Reset file interruption flag */
bctu = bctr; /* Switch to negotiated block check */
resetc(); /* Reset global counters. */
#ifdef XPRKERMIT
ST_Display_CRC(bctu);
#endif
/* Is there a file to send in an uncancelled batch? */
if (!cz && gnfile(filnam, sizeof(filnam)) > 0) {
if (sfile() < 0) { ERR("sfile"); } /* Yes, open it, send F packet */
else BEGIN ssdat; /* and if no error, switch state. */
} else { /* No (more) files to send */
if (seot() < 0) { ERR("seot"); } /* so send B packet */
else BEGIN sseot; /* and switch to sseot state. */
}
}
<ssdat>Y { /* - Send Data State - */
int x;
if (rln == 1 && *rdatap == 'X') /* Did ACK contain X as data? */
cx = 1; /* Yes, set control-x flag. */
else if (rln == 1 && *rdatap == 'Z') /* Did ACK contain Z as data? */
cz = 1; /* Yes set control-z flag. */
/* Check here for cancellation of transfer and data left to send. */
if (cx || cz || (x = sdata()) == 0) {
if (seof((cx || cz) ? "D" : "") < 0) { /* If not, send Z packet. */
ERR("seof");
}
else BEGIN ssfil; /* and go back to ssfil state. */
} else if (x < 0) { ERR("sdata"); } /* Handle file i/o errors. */
}
<sseot>Y { RESUME; } /* - Send B, done. */
/* Dynamic states, receiving file(s) */
<srini,srgen>S {
resetc();
spar(rdatap);
(void) ack1(rpar());
bctu = bctr;
BEGIN srfil;
}
<srfil>B { (void) ack(); RESUME; }
<srfil>F { if (rcvfil() < 0) { ERR("rcvfil"); } else { (void) ack(); BEGIN srdat; } }
<srdat>D {
if (cx)
ack1("X");
else if (cz)
ack1("Z");
else if (decode() < 0) {
ERR("Error writing data");
} else
(void) ack();
}
<srdat>Z {
if (reof(filnam) < 0) { /* Close & dispose of the file */
errpkt("Can't close file"); /* If problem, send error message */
RESUME; /* and quit */
} else { /* otherwise */
ack(); /* acknowledge the EOF packet */
BEGIN srfil; /* and await another file */
}
}
/* Dynamic states, server mode */
<sipkt>Y { /* Got ACK for I packet */
spar(rdatap); /* Set parameters from it */
start = 'E'; /* Force entry into next state */
}
<sipkt>E { /* Got E for I packet */
if (ssc) {
if (scmd(ssc,cmarg) < 0) { ERR("scmd"); }
else BEGIN srgen;
} else {
if (scmd('R',cmarg) < 0) { ERR("scmd"); }
else BEGIN srini;
}
}
<srgen>Y { xflag = 1; decode(); RESUME; }
<srgen,srfil>X { xflag = 1; decode(); ack(); BEGIN srdat; }
/* Error state */
E { error((char *) rdatap);
(void) closif();
(void) closof(1); /* close files, discarding output */
RESUME; }
. { error("Unexpected packet type"); RESUME; }
/* Handle unwanted packet types. */
%%
/*lint -restore -esym(551,tlci,tlco,flci,flco,rptn,atcapu,tfc) */
static void
errpkt(char *msg) {
spack('E', seq, (int) strlen(msg), (CHAR *) msg);
}
static
int
input() { /* Return packets */
int type, try;
if (start != 0) { /* Start state in effect? */
type = start; /* Yes, call it a packet type, */
start = 0; /* nullify the start state, */
return(type); /* and return the type. */
}
for (try = 0; ; try++) {
type = rpack(); /* No start state, read a packet. */
/*
* Some checks we do every packet.
*/
if (try > limit) { /* If too mahy tries, */
strcpy((char *) data, "Too many retries"); /* give up */
rdatap = data; /* Make up pretend 'E' packet */
type = 'E'; /* and return it */
break;
}
#ifdef XPRKERMIT
if (xchkmisc)
(void) (*xchkmisc)();
#endif
if (chkint() < 0) { /* Check for console interrupt. */
errpkt("User cancelled.");
strcpy((char *) data, "User cancelled.");
rdatap = data;
type = 'E';
break;
}
/*
* Now see if we got the packet we want.
*/
if (rsn == seq && strchr("TQN",type) == 0) /* packet we want */
break;
else if (type == 'N' && rsn == ((seq + 1) & 63)) {
/* NAK for next packet */
type = 'Y'; /* is ACK for current. */
break;
} else { /* Otherwise, */
(void) resend(); /* resend previous packet. */
}
}
ttflui(); /* Got a good one, clear buffer. */
return(type); /* Return its type. */
}
static
nxtpkt() {
seq = (seq + 1) & 63; /* Next packet number, mod 64 */
}
/* R E S E T C -- Reset per-transaction character counters */
static void resetc(void) {
tfc = tlci = tlco = 0; /* Total file chars, line chars in & out */
timeouts = 0; /* Timeouts */
}
static
tinit() { /* Transaction initialization */
seq = 0; /* Start off with packet 0 */
ebqflg = 0; /* 8-bit quoting off */
sq = 'Y'; /* Normal 8-bit quote bid */
rqf = -1; /* Flag other's bid not yet seen */
rptflg = 0; /* No repeat counts by default */
bctu = 1; /* Block check to use back to 1 */
xflag = 0; /* Output normally to file */
memstr = 0; /* Reset memory-string flag */
memptr = NULL; /* and poiinter */
osp = NULL; /* ... */
cx = cz = 0; /* Reset interrupt flags */
*filnam = *sndpkt = *rcvpkt = '\0'; /* Clear string buffers */
spsiz = rpsiz; /* Initial send-packet size */
}
static
error(s) char *s; { /* Fatal error */
if (local) { /* If in local mode */
screen(SCR_EM,0,0L,s); /* Type message on console */
} else { /* Otherwise, */
(void) spack('E',seq,(int) strlen(s), (CHAR *) s); /* Send in error packet. */
}
return;
}
static
ack() {
int x; /* Empty acknowledgement */
x = spack('Y',seq,0,(CHAR *) ""); /* Send the packet */
nxtpkt(); /* Increment the packet number */
return(x);
}
static
ack1(s) char *s; {
int x; /* Acknowledgement with data */
x = spack('Y',seq,(int)strlen(s),(CHAR *)s);
nxtpkt(); /* Increment packet number */
return(x);
}
static
nak() { /* Negative acknowledgement */
return(spack('N',seq,0,(CHAR *)"")); /* Never has data! */
}
/* Functions used by file sender. */
/* sinit()--start the transaction by filling in the initialization string
* and sending it in an S packet.
*/
static
sinit(c) char c; {
char *s;
gninit(); /* Initialize gnfile() function. */
s = rpar();
if (local == 0 && c == 'S' && server == 0) {
#ifdef XPRKERMIT
ST_Display_String(STMsg,"Escape back to local system, RECEIVE command");
#else
tmsgl("Escape back to local system, give RECEIVE command...");
#endif
sleep(delay);
}
return(spack(c,seq,(int)strlen(s),(CHAR *)s));
}
/*
* scmd() -- send a preformatted Kermit server command string.
*/
static
scmd(t, s) char t, *s; { /* Send a packet of the given type */
encstr(s); /* Encode the command string */
spack(t,seq,size,data);
}
/* rinit() -- do whatever is needed to initialize receive. Now a no-op.
*/
static
rinit()
{
}
/* sfile() -- open the file and send the File-Header packet. Assumes that
* the global string pointer filnam references the file name.
*/
static
sfile() {
int x;
char pktnam[MAXNAMELEN];
if (zopeni(filnam) < 0) /* Try to open file. */
return -1;
zltor(filnam,pktnam); /* OK, convert name. */
x = encstr(pktnam); /* Encode the result */
if (local) { /* If in local mode, */
#ifdef XPRKERMIT
ST_Display_String(STFile, pktnam);
ST_Display_String(STUplSize, filnam);
#else
tmsg("Sending "); /* let user know we're */
tmsg(filnam); /* sending this file */
tmsg(" as "); /* under */
tmsgl(pktnam); /* this name */
#endif
}
first = 1; /* Flag beginning of file */
ffc = flci = flco = 0; /* Zero per-file char count */
nxtpkt(); /* Increment packet number */
return(spack((xpkt ? 'X' : 'F'),seq,x,data)); /* Send packet */
}
/* sdata() -- get next packet's worth of data */
static
sdata() {
int x;
if (spsiz > 94) /* Long packets */
x = getpkt(spsiz - bctu - 6);
else /* Short packets. */
x = getpkt(spsiz - bctu - 3);
if (x == 0) /* If no data left to send, */
return(0); /* return EOF indication. */
nxtpkt();
return(spack('D',seq,x,data)); /* Send the data packet */
}
/* seof -- close the input file and send a Z packet. */
static
seof(s) char *s; {
if (closif() < 0) /* Try to close the file. */
return -1; /* On error, return failure. */
else { /* Otherwise, */
#ifdef XPRKERMIT
if (local) ST_Display_String(STMsg,"OK");
#else
if (local) tmsgl("OK"); /* if local, reassure user. */
#endif
nxtpkt();
return(spack('Z',seq,(int)strlen(s),(CHAR *) s)); /* Send Z packet */
}
}
/* seot -- send B packet. */
static
seot() {
nxtpkt();
#ifdef XPRKERMIT
if (local) ST_Display_String(STMsg,"Done");
#else
if (local) tmsgl("Done");
#endif
return(spack('B',seq,0,(CHAR *) ""));
}
static
rcvfil() { /* Receive a file */
ffc = flci = flco = 0; /* Initialize per-file char count */
decstr(filnam); /* Decode name */
zrtol(filnam,myname,warn); /* Convert to local form. */
if (zopeno(myname) < 0)
return -1;
else { /* File open OK, give message. */
if (local && !xflag) {
#ifdef XPRKERMIT
ST_Display_String(STFile,myname);
ST_Display_CRC(bctu);
#else
tmsg("Receiving "); tmsg(filnam); tmsg(" as "); tmsgl(myname);
#endif
}
return 0;
}
}
/* R E O F -- Receive End Of File */
static
reof(f) char *f; {
int x;
char *p;
if (cx == 0) cx = (*rdatap == 'D'); /* Got discard directive? */
x = closof((cx || cz) && (keep == 0));
if (cx || cz) {
cx = 0;
if (keep)
p = "*** Incomplete file";
else
p = "*** Discarding file";;
screen(SCR_EM, 0, 0L, p);
} else {
fstats();
}
*filnam = '\0';
return(x);
}
static void /* Update statistics */
fstats() {
tfc += ffc;
}
static
closof(nokeep) int nokeep; { /* Close output file, but */
if (xflag) return 0; /* not if it's the screen. */
if (zcloso(nokeep) < 0)
return -1;
return 0;
}
static
spack(type,n,len,d) char type; int n, len; CHAR *d; {
int i = 0, j, k;
unsigned int x;
CHAR *sohp; /* Mark start of packet data. */
for (i = 0; i < spadn; i++)
sndpkt[i] = spadc; /* Do requested padding */
sohp = sndpkt + i;
sndpkt[i++] = smark; /* Packet mark */
k = i++; /* Remember this place */
sndpkt[i++] = tochar(n); /* Sequence number */
sndpkt[i++] = (CHAR) type; /* Packet type */
j = len + bctu; /* True length */
if (j > 92) { /* Long packet? */
sndpkt[k] = tochar(0); /* Set LEN to 0 */
sndpkt[i++] = tochar(j / 95); /* High part of length */
sndpkt[i++] = tochar(j % 95); /* Low part of length */
sndpkt[i] = '\0'; /* Header checksum */
sndpkt[i++] = tochar(chk1(sndpkt+k));
} else
sndpkt[k] = tochar(j+2); /* True length. */
for (j = len; j > 0; j--) { /* Data */
sndpkt[i++] = *d++;
}
sndpkt[i] = '\0'; /* Null terminate. */
switch (bctu) {
case 1: /* Type 1 - 6 bit checksum */
sndpkt[i++] = tochar(chk1(sndpkt+k));
break;
case 2: /* Type 2 - 12 bit checksum */
x = chksum(sndpkt+k);
sndpkt[i++] = tochar((x >> 6) & 077);
sndpkt[i++] = tochar(x & 077);
break;
case 3: /* Type 3 - 16 bit CRC-CCITT */
x = chk3(sndpkt + k);
sndpkt[i++] = tochar((x >> 12) & 017);
sndpkt[i++] = tochar((x >> 6) & 077);
sndpkt[i++] = tochar(x & 077);
break;
default:
return -1; /* Error message here? */
}
sndpkt[i++] = seol; /* End of line */
sndpkt[i] = '\0'; /* Null string-terminat or. */
sndpkl = i; /* Remember length. */
i = ttol((char *) sndpkt,sndpkl); /* Send the packet. */
tlco += sndpkl; /* Count characters output. */
flco += sndpkl;
screen(SCR_PT, type, (long) n, (char *) sohp);
return(i);
}
static
resend() {
int x;
if (*sndpkt)
x = ttol((char *) sndpkt,sndpkl); /* Send previous packet */
else
x = nak(); /* or NAK if none */
if (local && !xflag) tchar('%'); /* Let the user know. */
return(x);
}
static unsigned
chk1(packet) CHAR *packet; { /* Compute Kermit's */
unsigned s, t; /* 1-character block check. */
s = chksum(packet); /* Get the arithmetic sum. */
t = (((s & 192) >> 6) + s) & 63; /* Fold it into 6 bits. */
return(t);
}
static unsigned
chksum(p) CHAR *p; { /* Compute the checksum. */
unsigned m;
long s;
m = (parity) ? 0177 : 0377; /* Mask for parity bit. */
s = 0;
for (; *p != '\0'; p++) /* For each character, */
s += *p & m; /* accumulate the sum, */
return(s & 07777); /* and then return it. */
}
/*
* rpack reads a packet and returns the packet type, or else Q if the
* packet was invalid, or T if a timeout occured. Upon successful return,
* sets the global variables:
* rsn - the received sequence number
* rln - length of the received data field
* rdatap - a pointer to the null-terminated contents of the data field
*/
static
rpack() {
int i, j, type, rlnpos;
unsigned x;
CHAR pbc[4]; /* Packet block check. */
CHAR *sohp; /* Start of packet data. */
rsn = rln = -1; /* In case of failure. */
*rcvpkt = '\0'; /* Initialize receive buffer. */
j = ttinl((char *) rcvpkt,MAXRP,reol,stimo);
if (j < 0) {
if (j == -1) {
screen(SCR_PT, 'T', (long) rsn, "");
return('T'); /* Timed out. */
} else
return(j);
}
tlci += j; /* Count received characters. */
flci += j; /* On per-file basis. */
for (i = 0; rcvpkt[i] != rmark && (i < j); i++) /* Find mark. */
;
if (i == j) return('Q'); /* If no mark, bad packet. */
sohp = rcvpkt+i;
rlnpos = ++i; /* Got it, remember position. */
if ((j = unchar(rcvpkt[i++])) == 0) { /* Long packet? */
j = rlnpos + 5; /* Yes, check header */
if (j > MAXRP) return('Q'); /* Be defensive. */
x = rcvpkt[j]; /* Remember header checksum */
rcvpkt[j] = '\0';
if (unchar(x) != chk1(rcvpkt+rlnpos)) /* Check header */
return('Q');
rcvpkt[j] = (CHAR) x; /* Restore packet */
rln = unchar(rcvpkt[j-2]) * 95 + unchar(rcvpkt[j-1]) - bctu;
j = 3;
} else {
rln = j - bctu - 2; /* Regular packet */
j = 0; /* No extended header */
}
rsn = unchar(rcvpkt[i++]); /* Sequence number. */
type = rcvpkt[i++]; /* Packet type */
i += j; /* Skip extended header, if any */
rdatap = rcvpkt + i; /* The data itself. */
j = rln + i; /* Position of block check. */
if (j > MAXRP)
return('Q'); /* Be defensive! */
for (i = 0; i < bctu; i++) /* Copy the block check. */
pbc[i] = rcvpkt[j+i];
rcvpkt[j] = '\0';
switch (bctu) { /* Which block check type? */
case 1:
if (unchar(*pbc) != chk1(rcvpkt+rlnpos)) return('Q');
break;
case 2:
x = unchar(*pbc) << 6 | unchar(pbc[1]);
if (x != chksum(rcvpkt+rlnpos)) return('Q');
break;
case 3:
x = unchar(*pbc) << 12 | unchar(pbc[1]) << 6 | unchar(pbc[2]);
if (x != chk3(rcvpkt+rlnpos)) return('Q');
break;
default:
error("Impossible block check type.");
}
screen(SCR_PT, type, (long) rsn, (char *) sohp);
return type; /* Otherwise, return packet type */
}
/*
* CHK3
* Calculate the 16-bit CRC of a null-terminated string using a
* byte-oriented tableless algorithm devised by Andy Lowry (Columbia
* University). The magic number 010201 is derived from the CRC-CCITT
* polynomial x^16+x^12+x^5+1.
*/
static unsigned
chk3(s) CHAR *s; {
unsigned c, q;
unsigned long crc = 0;
while ((c = *s++) != '\0') {
if (parity) c &= 0177;
q = (crc ^ c) & 017; /* Low order nybble */
crc = (crc >> 4) ^ (q * 010201);
q = (crc ^ (c >> 4)) & 017; /* High order nybble */
crc = (crc >> 4) ^ (q * 010201);
}
return((unsigned) crc);
}
/* G E T P K T -- Fill a packet data field */
/*
Gets characters from the current source -- file or memory string.
Encodes the data into the packet, filling the packet optimally.
Set first = 1 when calling for the first time on a given input stream
(string or file).
Uses global variables:
t -- current character.
first -- flag: 1 to start up, 0 for input in progress, -1 for EOF.
next -- next character.
data -- pointer to the packet data buffer.
size -- number of characters in the data buffer.
memstr - flag that input is coming from a memory string instead of a file.
memptr - pointer to string in memory.
(*sx)() character set translation function
Returns the size as value of the function, and also sets global "size",
and fills (and null-terminates) the global data array. Returns 0 upon eof.
Rewritten by Paul W. Placeway (PWP) of Ohio State University, March 1989.
Incorporates old getchx() and encode() inline to eliminate function calls,
uses buffered input for much-improved efficiency, and clears up some
confusion with line termination (CRLF vs LF vs CR).
*/
/*
* Below defines for using the C Kermit 5A getpkt() routine in this module.
*/
#define NLCHAR '\n'
#define LF '\n'
#define SP ' '
#define DEL 0177
#define debug(a,b,c,d)
#define binary (!text)
#define cxseen cx
#define fmask 0377
#define myctlq sctlq
#define NOCSETS
static CHAR t, next;
static int rptn;
getpkt(bufmax) int bufmax; { /* Fill one packet buffer */
register CHAR rt = t, rnext = next; /* register shadows of the globals */
register CHAR *dp, *odp, *p1, *p2; /* pointers... */
register int x; /* Loop index. */
register int a7; /* Low 7 bits of character */
static CHAR leftover[7] = { '\0', '\0', '\0', '\0', '\0', '\0', '\0' };
if (first == 1) { /* If first time thru... */
ffc = 0;
first = 0; /* remember, */
*leftover = '\0'; /* discard any interrupted leftovers, */
/* get first character of file into t, watching out for null file */
if (memstr) {
if ((rt = *memptr++) == '\0') { /* end of string ==> EOF */
first = -1;
size = 0;
debug(F100,"getpkt: empty string","",0);
return (0);
}
} else {
if ((x = zminchar()) < 0) { /* End of file or input error */
first = -1;
size = 0;
if (x == -2) { /* Error */
debug(F100,"getpkt: input error","",0);
cxseen = 1;
} else debug(F100,"getpkt: empty file","",0);
return(0);
}
ffc++; /* Count a file character */
rt = x;
debug(F101,"getpkt zminchar","",rt);
}
#ifndef NOCSETS
debug(F101,"getpkt about to call translate function","",rt);
if (!binary && sx) rt = (*sx)(rt); /* Translate */
debug(F101," translate function returns","",rt);
#endif /* NOCSETS */
rt &= fmask; /* bytesize mask */
/* PWP: handling of NLCHAR is done later (in the while loop)... */
} else if ((first == -1) && (*leftover == '\0')) /* EOF from last time? */
return(size = 0);
/* Do any leftovers */
dp = data;
for (p1 = leftover; (*dp = *p1) != '\0'; p1++, dp++) /* copy leftovers */
;
*leftover = '\0';
if (first == -1)
return(size = (dp - data)); /* Handle final leftovers */
/* Now fill up the rest of the packet. */
rpt = 0; /* Clear out any old repeat count. */
while (first > -1) { /* Until EOF... */
if (memstr) { /* get next character */
if ((rnext = *memptr++) == '\0') { /* end of string ==> EOF */
first = -1; /* Flag eof for next time. */
} else {
#ifndef NOCSETS
if (!binary && sx) rnext = (*sx)(rnext); /* Translate */
#endif /* NOCSETS */
ffc++;
rnext &= fmask; /* Bytesize mask. */
}
} else {
if ((x = zminchar()) < 0) { /* End of file or error */
first = -1; /* Flag eof for next time. */
if (x == -2) cxseen = 1; /* If error, cancel this file */
} else {
#ifndef NOCSETS
if (!binary && sx) x = (*sx)(x); /* Translate */
#endif /* NOCSETS */
rnext = x & fmask; /* Bytesize mask. */
ffc++; /* Count it */
}
}
/* PWP: handling of NLCHAR is done in a bit... */
odp = dp; /* Remember current position. */
/* PWP: the encode() procedure, in-line (for speed) */
if (rptflg) { /* Repeat processing? */
if (
#ifdef NLCHAR
/*
* PWP: this is a bit esoteric, so bear with me.
* If the next char is really CRLF, then we cannot
* be doing a repeat (unless CR,CR,LF which becomes
* "~ <n-1> CR CR LF", which is OK but not most efficient).
* I just plain don't worry about this case. The actual
* conversion from NL to CRLF is done after the rptflg if...
*/
(binary || (rnext != NLCHAR)) &&
#endif /* NLCHAR */
rt == rnext && (first == 0)) { /* Got a run... */
if (++rpt < 94) { /* Below max, just count */
continue; /* go back and get another */
}
else if (rpt == 94) { /* Reached max, must dump */
*dp++ = rptq;
*dp++ = tochar(rpt);
rptn += rpt;
rpt = 0;
}
} else if (rpt == 1) { /* Run broken, only 2? */
/*
* PWP: Must encode two characters. This is handled
* later, with a bit of blue smoke and mirrors, after
* the first character is encoded.
*/
} else if (rpt > 1) { /* More than two */
*dp++ = rptq; /* Insert the repeat prefix */
*dp++ = tochar(++rpt); /* and count. */
rptn += rpt;
rpt = 0; /* Reset repeat counter. */
}
}
#ifdef NLCHAR
/*
* PWP: even more esoteric NLCHAR handling. Remember, at
* this point t may still be the _system_ NLCHAR. If so,
* we do it here.
*/
if (!binary && (rt == NLCHAR)) {
*dp++ = myctlq; /* just put in the encoding directly */
*dp++ = 'M'; /* == ctrl(CR) */
if ((dp-data) <= bufmax) odp = dp; /* check packet bounds */
rt = LF;
}
#endif
/* meta control stuff fixed by fdc */
a7 = rt & 0177; /* Low 7 bits of character */
if (ebqflg && (rt & 0200)) { /* Do 8th bit prefix if necessary. */
*dp++ = ebq;
rt = a7;
}
if ((a7 < SP) || (a7 == DEL)) { /* Do control prefix if necessary */
*dp++ = myctlq;
rt = ctrl(rt);
}
if (a7 == myctlq) /* Prefix the control prefix */
*dp++ = myctlq;
if ((rptflg) && (a7 == rptq)) /* If it's the repeat prefix, */
*dp++ = myctlq; /* quote it if doing repeat counts. */
if ((ebqflg) && (a7 == ebq)) /* Prefix the 8th bit prefix */
*dp++ = myctlq; /* if doing 8th-bit prefixes */
*dp++ = rt; /* Finally, insert the character */
if (rpt == 1) { /* Exactly two copies? */
rpt = 0;
p2 = dp; /* save current size temporarily */
for (p1 = odp; p1 < p2; p1++) /* copy the old chars over again */
*dp++ = *p1;
if ((p2-data) <= bufmax) odp = p2; /* check packet bounds */
}
rt = rnext; /* Next is now current. */
if ((dp-data) >= bufmax) { /* If too big, save some for next. */
size = (dp-data);
*dp = '\0'; /* mark (current) the end. */
if ((dp-data) > bufmax) { /* if packet is overfull */
for (p1 = leftover, p2=odp; (*p1 = *p2) != '\0'; p1++,p2++)
;
debug(F111,"getpkt leftover",leftover,size);
debug(F101," osize","",(odp-data));
size = (odp-data); /* Return truncated packet. */
*odp = '\0'; /* mark real end */
} else { /* If the packet is exactly full, */
debug(F101,"getpkt exact fit","",size);
}
t = rt; next = rnext; /* save for next time */
return(size);
}
} /* Otherwise, keep filling. */
size = (dp-data);
*dp = '\0'; /* mark (current) the end. */
debug(F111,"getpkt eof/eot",data,size); /* Fell thru before packet full, */
return(size); /* return partially filled last packet. */
}
/*
* Decodes the data pointed to by the global pointer rdatap.
*/
static
decode() {
unsigned a, a7, b8;
while ((a = *rdatap++) != '\0') {
rpt = 1; /* Initialize repeat count. */
if (rptflg) { /* Repeat processing? */
if (a == rptq) { /* Yes, have repat prefix? */
rpt = unchar(*rdatap++); /* Yes, get count. */
a = *rdatap++; /* and following character. */
}
}
b8 = 0; /* Assume 8th bit not on. */
if (ebqflg) { /* Doing 8th-bit prefixing? */
if (a == ebq) { /* Yes, have 8th-bit prefix? */
b8 = 128; /* Yes, remember bit 8 on */
a = *rdatap++; /* and get following character. */
}
}
if (a == rctlq) { /* Is it control prefix? */
a = *rdatap++; /* Yes, get next character */
a7 = a & 127; /* and its low 7 bits. */
if (a7 > 62 && a7 < 96) /* Encoded control character? */
a = ctrl(a); /* Yes, controllify */
}
a |= b8; /* OR in the 8th bit. */
if (text)
if (a == '\r') continue; /* Discard carriage returns. */
for (; rpt > 0; rpt--) {
if (osp)
*osp++ = a;
else if (xflag) /* If flagged... */
tchar(a); /* ... output to terminal */
else {
if (zmchout(a) < 0) return -1; /* Output the character. */
ffc++;
}
}
}
tflush(); /* Flush out message (if xflag) */
return(0);
}
static
encstr(s) char *s; { /* Fill a packet from the string */
first = 1; /* Start lookahead. */
memptr = (CHAR *) s; /* Set input string pointer */
memstr = 1;
(void) getpkt(spsiz); /* Fill a packet */
memstr = 0; /* Reset input string pointer */
return(size); /* Return data field length */
}
static
decstr(s) char *s; { /* Decode packet data into a string */
osp = s; /* Set output string pointer */
(void) decode(); /* Decode the string */
*osp = '\0'; /* Terminate the string */
osp = NULL; /* Reset output string pointer */
}
static
spar(s) CHAR *s; { /* Set parameters */
int x;
s--; /* Line up with field numbers. */
/* Limit on size of outbound packets */
x = (rln >= 1) ? unchar(s[1]) : 80;
spsiz = (x < 10) ? 80 : x;
/* Timeout on inbound packets */
x = (rln >= 2) ? unchar(s[2]) : 5;
stimo = (x < 0) ? 5 : x;
/* Outbound padding */
spadn = 0; spadc = '\0';
if (rln >= 3) {
spadn = unchar(s[3]);
if (rln >= 4)
spadc = ctrl(s[4]);
}
/* Outbound packet terminator */
seol = (rln >= 5) ? unchar(s[5]) : '\r';
if (seol < 2 || seol > 31) seol = '\r';
/* Control prefix */
x = (rln >= 6) ? s[6] : '#';
rctlq = ((x > 32 && x < 63) || (x > 95 && x < 127)) ? x : '#';
/* 8th-bit quoting */
rq = (rln >= 7) ? s[7] : 0;
if (rq == 'Y') rqf = 1;
else if ((rq > 32 && rq < 63) || (rq > 95 && rq < 127)) rqf = 2;
else rqf = 0;
switch (rqf) {
case 0: ebqflg = 0; break;
case 1: if (parity) { ebqflg = 1; ebq = '&'; } break;
case 2: if (ebqflg = (ebq == sq || sq == 'Y')) ebq = rq;
}
/* Block check */
x = 1;
if (rln >= 8) {
x = s[8] - '0';
if (x < 1 || x > 3) x = 1;
}
bctr = x;
/* Repeat prefix */
if (rln >= 9) {
rptq = s[9];
rptflg = ((rptq > 32 && rptq < 63) || (rptq > 95 && rptq < 127));
} else rptflg = 0;
/* Capabilities */
atcapu = lpcapu = swcapu = 0; /* No capabilities by default */
if (rln >= 10) {
x = unchar(s[10]);
atcapu = (x & atcapb) && atcapr; /* Attribute packets */
lpcapu = (x & lpcapb) && lpcapr; /* Long packets */
swcapu = (x & swcapb) && swcapr; /* Sliding windows */
for (capas = 10; (unchar(s[capas]) & 1) && (rln >= capas); capas++)
; /* Skip to capas + 1 */
}
/* Long packets */
if (lpcapu) { /* Flag set above */
if (rln > capas+2) {
x = unchar(s[capas+2]) * 95 + unchar(s[capas+3]);
spsiz = x > MAXSP ? MAXSP : x;
}
if (spsiz < 10) spsiz = 80; /* Be defensive... */
}
/* Sliding windows */
if (swcapu) {
if (rln > capas+1) {
x = unchar(s[capas+1]);
wsize = x > MAXWS ? MAXWS : x;
} else
wsize = 1;
}
}
/* Fill the array with my send-init parameters */
static char *
rpar() {
data[1] = tochar(DRPSIZ); /* Biggest packet I can receive */
data[2] = tochar(rtimo); /* When I want to be timed out */
data[3] = tochar(rpadn); /* How much padding I need */
data[4] = ctrl(rpadc); /* Padding character I want */
data[5] = tochar(reol); /* End-of-Line character I want */
data[6] = sctlq; /* Control-Quote character I send */
switch(rqf) { /* 8th-bit prefix */
case -1:
case 1: if (parity) ebq = sq = '&'; break;
case 0:
case 2: break;
}
data[7] = sq;
data[8] = bctr + '0'; /* Block Check Type */
if (rptflg) data[9] = rptq; else data[9] = '~';
data[10] = tochar(atcapr?atcapb:0 | lpcapr?lpcapb:0 | swcapr?swcapb:0);
capas = 10;
data[capas+1] = tochar(swcapr ? wsize : 0); /* Window size */
data[capas+2] = tochar(rpsiz / 95); /* Long packet size */
data[capas+3] = tochar(rpsiz % 95); /* ... */
data[capas+4] = '\0';
return((char *) (data+1)); /* Return a pointer to the string */
}
/*
* proto()--Kermit protocol entry point. Set your start state and call
* this, NOT wart(). Modified to set long packets capability on the
* basis of the packet size set in the external user interface.
*/
void
proto()
{
#ifdef XPRKERMIT
struct XPR_UPDATE xpru;
xpru.xpru_protocol = "XPR-Kermit";
xpru.xpru_updatemask = XPRU_PROTOCOL;
calla(xupdate, &xpru);
zclear();
#endif
sndpkt = malloc(MAXSP+100);
rcvpkt = malloc(MAXRP+200);
data = malloc(MAXSP+50);
zinbuffer = malloc(INBUFSIZE);
zoutbuffer = malloc(OBUFSIZE);
if (urpsiz > 94) { /* Long packets? */
rpsiz = (urpsiz > MAXRP - 20 ? MAXRP - 20 : urpsiz);
lpcapr = 1; /* Request long packets */
} else { /* No long packets */
lpcapr = 0;
if (urpsiz < 10) /* Too small? */
rpsiz = 80;
else
rpsiz = DRPSIZ;
}
urpsiz = rpsiz;
cx = cz = 0; /* Haven't aborted yet */
if (bctr < 1)
bctr = 1; /* Legal block check? */
if (bctr > 3)
bctr = 3;
if (sndpkt == NULL || rcvpkt == NULL || data == NULL || zinbuffer == NULL ||
zoutbuffer == NULL)
#ifdef XPRKERMIT
ST_Display_String(STMsg,"Can't allocate memory");
#else
tmsgl("Can't allocate memory for Kermit!!");
#endif
else
wart();
if (sndpkt) { free(sndpkt); sndpkt = NULL; }
if (rcvpkt) { free(rcvpkt); rcvpkt = NULL; }
if (data) { free(data); data = NULL; }
if (zinbuffer) { free(zinbuffer); zinbuffer = NULL; }
if (zoutbuffer) { free(zoutbuffer); zoutbuffer = NULL; }
}
/*
* That ends the system-independent Kermit modules. What follows
* are the system-dependent ones.
*/
/*
* File I/O modules for XPR Kermit.
*/
#define EOF -1
typedef struct FILE FILE;
static FILE *ifp = NULL, *ofp = NULL;
static
zclear()
{
ifp = NULL; ofp = NULL;
}
static
zopeni(name) char *name; {
zincnt = 0;
ifp = (FILE *) callaa(xfopen, name, "r");
if (ifp == NULL)
return -1;
else
return 0;
}
#include <dos/dos.h>
#include <clib/dos_protos.h>
#include <pragmas/dos_pragmas.h>
static
zopeno(name) char *name; {
zoutcnt = 0;
zoutptr = zoutbuffer;
ofp = (FILE *) callaa(xfopen, name, "w");
if (ofp == NULL)
return -1;
else
return 0;
}
static
zclosi() {
int x;
if (ifp == NULL)
return 0;
x = calla(xfclose, ifp);
ifp = NULL;
if (x < 0)
return -1;
else
return 0;
}
static
zcloso(discard) int discard; {
int x;
if (ofp == NULL)
return 0;
if (zoutcnt > 0)
if (zoutdump() != 0)
return -1;
x = calla(xfclose, ofp);
ofp = NULL;
if (x < 0)
return -1;
else if (discard) {
if (xunlink != NULL) {
if (calla(xunlink, myname) < 0) {
return -1;
}
}
return 0;
}
return 0;
}
#include <ctype.h>
extern int convert; /* 0 for literal files, 1 for translate */
/* Z R T O L -- Convert remote filename into local form */
/* For AMIGA, this means changing uppercase letters to lowercase. */
zrtol(name,name2,mywarn) char *name, *name2; int mywarn; {
if (convert) {
for ( ; *name != '\0'; name++) {
*name2++ = isupper(*name) ? tolower(*name) : *name;
}
*name2 = '\0';
} else
strcpy(name2,name);
}
/* name from local to remote format */
static
zltor(s1,s2) char *s1, *s2; {
char *EndPath();
char dotseen = 0;
if (!convert)
strcpy(s2, s1);
else {
s1 = EndPath(s1); /* strip dir/disk */
while (*s1 != '\0') {
if (islower(*s1))
*s2 = toupper(*s1);
else if (isalnum(*s1))
*s2 = *s1;
else if (*s1 == '.')
if (!dotseen) {
dotseen = 1;
*s2 = *s1;
} else
*s2 = 'X';
/* else a character we're not prepared to handle. */
s1++; s2++;
}
*s2 = '\0';
}
}
/*
* System-dependent function to do buffered input and output.
* Text conversion now done in the packet routines.
*/
static
zinfill(void) {
zincnt = calladda(xfread, zinbuffer, sizeof (char), INBUFSIZE, ifp);
if (zincnt == 0) return (-1); /* end of file */
zinptr = zinbuffer; /* set pointer to beginning, (== &zinbuffer[0]) */
zincnt--; /* one less char in buffer */
return((int)(*zinptr++) & 0377); /* because we return the first */
}
static
zoutdump(void) {
zoutptr = zoutbuffer; /* reset buffer pointer in all cases */
if (zoutcnt == 0) { /* nothing to output */
return(0);
} else if (zoutcnt < 0) { /* unexpected negative value */
zoutcnt = 0; /* reset output buffer count */
return(-1); /* and fail. */
}
if (calladda(xfwrite, zoutbuffer, sizeof (char), zoutcnt, ofp)) {
zoutcnt = 0; /* reset output buffer count */
return(0); /* things worked OK */
} else {
zoutcnt = 0; /* reset output buffer count */
return(-1); /* error return */
}
}
int ttol(s, n)
char *s;
int n;
{
long status;
status = callad(xswrite, s, (long) n);
if (status == 0) return(n);
else return(-1);
}
#include "timer.h"
#include <clib/exec_protos.h>
#include <pragmas/exec_pragmas.h>
#ifdef MYREAD
/* Private buffer for myread() and its companions. Not for use by anything
* else. ttflui() is allowed to reset them to initial values. ttchk() is
* allowed to read my_count.
*
* my_item is an index into mybuf[]. Increment it *before* reading mybuf[].
*
* A global parity mask variable could be useful too. We could use it to
* let myread() strip the parity on its own, instead of stripping sign
* bits as it does now.
*/
#define MYBUFLEN 256
static CHAR mybuf[MYBUFLEN]; /* Buffer, including push back */
static int my_count = 0; /* Number of chars still in mybuf */
static int my_item = -1; /* Last index read from mybuf[] */
/* myread() -- Efficient read of one character from communications line.
*
* Uses a private buffer to minimize the number of expensive read() system
* calls. Essentially performs the equivalent of read() of 1 character, which
* is then returned. By reading all available input from the system buffers
* to the private buffer in one chunk, and then working from this buffer, the
* number of system calls is reduced in any case where more than one character
* arrives during the processing of the previous chunk, for instance high
* baud rates or network type connections where input arrives in packets.
* If the time needed for a read() system call approaches the time for more
* than one character to arrive, then this mechanism automatically compensates
* for that by performing bigger read()s less frequently. If the system load
* is high, the same mechanism compensates for that too.
*
* myread() is a macro that returns the next character from the buffer. If the
* buffer is empty, mygetbuf() is called. See mygetbuf() for possible error
* returns.
*
* This should be efficient enough for any one-character-at-a-time loops.
* For even better efficiency you might use memcpy()/bcopy() or such between
* buffers (since they are often better optimized for copying), but it may not
* be worth it if you have to take an extra pass over the buffer to strip
* parity and check for CTRL-C anyway.
*
* Note that if you have been using myread() from another program module, you
* may have some trouble accessing this macro version and the private variables
* it uses. In that case, just add a function in this module, that invokes the
* macro.
*/
#define myread(timo) (--my_count < 0 ? mygetbuf(timo) : 255 & (int)mybuf[++my_item])
/*
* System-dependent mygetbuf() routine. Fills buffer with as many characters
* as it can get, provided it can get the next one within timo seconds.
* Returns the number of characters read, or 0 if none were read within
* timo seconds, or a negative value if some other error occured.
*/
static int
myfillbuf(int timo) {
long n;
/*
* Find out how many characters are available, if any.
* We can only do this if xsquery is non-NULL.
*/
if (xsquery != NULL && (n = (*xsquery)()) > 0) {
calladd(xsread, mybuf, n, 0L);
return n;
} else
/*
* xsread() returns exactly what we want: zero if no
* character is received within timo seconds, and -1
* on error. Might as well ask for a bufferful,
* provided first one arrives soon enough.
*/
return(calladd(xsread, &mybuf[0], (long) MYBUFLEN ,
timo*1000000L));
}
/* mygetbuf(timo) -- Fill buffer for myread() and return first character,
* within timo seconds.
*
* This function is what myread() uses when it can't get the next character
* directly from its buffer. First, it calls a system dependent myfillbuf()
* to read at least one new character into the buffer, and then it returns
* the first character just as myread() would have done. This function also
* is responsible for all error conditions that myread() can indicate.
*
* Returns: When OK => a positive character, 0 or greater.
* When TIMEOUT=> -1
* When error => -2.
*
*/
static int
mygetbuf(int timo) {
my_count = myfillbuf(timo);
if (my_count <= 0)
return(my_count - 1);
--my_count;
return(255 & (int)mybuf[my_item = 0]);
}
#endif /*MYREAD*/
/*
* T T I N L
*
* Return a line in the buffer pointed to by s from the serial line.
* The line must end with eol and be no longer than max characters.
* timeout is a timeout interval in seconds.
*
* Returns
* > 0 Success--number of characters read
* = 0 Shouldn't happen!
* -1 Timeout
* -2 Error of some other kind
*
* This version for XPR Kermit. We ignore negative returns from
* the xpr_sread() function, preferring to take care of these
* via the timeout mechanism.
*/
int ttinl(s, max, eol, timeout)
char *s;
int max, timeout, eol;
{
int x = 0;
long i;
struct timerequest *Timereq;
unsigned mask;
*s = '\0';
mask = (parity ? 0177 : 0377);
/*
* Set up and post timer request for overall timeout.
* As a compromise between correct and efficient, I only check
* the overall timeout if a one-character read with a one second
* timeout fails first.
*/
if (!(Timereq = CreateTimer(UNIT_VBLANK)))
return -1;
Timereq->tr_time.tv_secs = timeout;
Timereq->tr_time.tv_micro = 0;
Timereq->tr_node.io_Command = TR_ADDREQUEST;
SendIO((struct IORequest *) Timereq);
#ifndef MYREAD
/*
* Willy Langeveld assures me that, since serial.device does its
* own buffering, calling xsread once for each character isn't
* going to take too long.
*
*/
for (x = 0; x < max; ) {
if ((i = calladd(xsread, s, 1L, 1000000L)) < 0) {
if (chkint() < 0) {
x = -2;
goto leave;
}
} else if (i == 1) {
x++;
if ((*s++ &= mask) == eol)
break;
} else if (CheckIO((struct IORequest *) Timereq)) { /* timeout */
x = -1; /* Flag timeout abort */
goto leave;
}
}
*s = '\0'; /* Normal termination */
leave: /* Common cleanup code */
AbortIO((struct IORequest *) Timereq);
WaitIO((struct IORequest *) Timereq);
DeleteTimer(Timereq);
return(x);
#else
/*
* The following call to myread sets up a 1 second per-character
* timeout. We have to be careful not to return from ttinl until
* the entire timeout interval has passed, however.
*/
for (x = 0; x < max; ) {
if ((*s = myread(1)) < -1) {
/*
* The following code deals with the case that
* xsread() returns a negative value if the
* user has clicked on the "Cancel File" or
* "Cancel Batch" gadgets. However, we don't
* want to abort receiving the current packet
* in this case; calling chkint() now sets
* the global cx and/or cz flags, but in the
* case of a complete abort, chkint() returns
* a negative value.
*/
if (chkint() < 0) {
x = -2; /* Error */
goto leave;
}
} else if (CheckIO((struct IORequest *) Timereq)) { /* timeout */
x = -1; /* Flag timeout abort */
goto leave;
} else if (*s >= 0) { /* Successful get of character */
x++;
if ((*s++ &= mask) == eol)
break;
}
}
*s = '\0'; /* Normal termination */
leave: /* Common cleanup code */
AbortIO((struct IORequest *) Timereq);
WaitIO((struct IORequest *) Timereq);
DeleteTimer(Timereq);
return(x);
#endif /*MYREAD*/
}
/*
* This is the XPR Kermit version of chkint. We allow multiple levels
* of interrupt, if available.
*/
int chkint(void) /* Check for console interrupts. */
{
int x;
x = (*xchkabort)();
switch (x) {
case 0: /* No abort pending */
return 0;
case 1: /* Abort file only. */
cx = 1;
break;
case 2: /* Abort file and batch. */
cx = cz = 1;
break;
case -1: /* Emergency escape -- kill all */
cx = cz = 1;
return -1;
default:
;
}
return 1;
}
void ttflui()
{
(void) (*xsflush)();
#ifdef MYREAD
my_count = 0;
my_item = -1;
#endif
}
void sleep(sec)
int sec;
{
struct timeval tv;
if (sec) {
tv.tv_secs = sec;
tv.tv_micro = 0;
MyDelay(&tv, UNIT_VBLANK);
}
}
#define MAXMESSAGE 50
static char message[MAXMESSAGE+1];
static int nmess = 0;
static long errors = 0, blocks = 0;
/* screen(f,c,n,s)
f - argument descriptor
c - a character or small integer
n - a long integer
s - a string.
Fill in this routine with the appropriate display update for the system.
This version for XPR Kermit.
*/
screen(f,c,n,s) int f; long n; char c; char *s; {
struct XPR_UPDATE xpru;
if (!local || xflag) return;
switch (f) {
case SCR_PT: /* Packet Transferred. */
if (c == 'Y') return; /* Don't bother with ACK's. */
xpru.xpru_packettype = c;
xpru.xpru_blocksize = strlen(s);
xpru.xpru_blocks = ++blocks;
if (c == 'D' || c == 'Z') { /* If data or EOF packet ... */
xpru.xpru_bytes = ffc; /* ... update transfer count */
xpru.xpru_updatemask = XPRU_BYTES;
} else if (c == 'T') { /* Timeout */
xpru.xpru_timeouts = timeouts++;
xpru.xpru_updatemask = XPRU_TIMEOUTS;
} else
xpru.xpru_updatemask = 0;
xpru.xpru_updatemask |= XPRU_PACKETTYPE | XPRU_BLOCKSIZE |
XPRU_BLOCKS;
break;
case SCR_EM: /* Error message. */
xpru.xpru_errormsg = s;
xpru.xpru_updatemask = XPRU_ERRORMSG;
break;
default:
return;
}
calla(xupdate, &xpru);
}
void tchar(c)
char c;
{
struct XPR_UPDATE xpru;
if (c == '%') { /* retry */
xpru.xpru_errors = ++errors;
xpru.xpru_updatemask = XPRU_ERRORS;
} else if (c == '#') { /* fake it */
xpru.xpru_errors = errors = 0;
xpru.xpru_blocks = blocks = 0;
nmess = 0; /* Make sure message empty. */
xpru.xpru_updatemask = XPRU_BLOCKS | XPRU_ERRORS;
} else {
message[nmess++] = c;
if (nmess >= MAXMESSAGE)
tflush();
return;
}
calla(xupdate, &xpru);
}
static
tflush()
{
struct XPR_UPDATE xpru;
if (nmess <= 0)
return;
message[nmess] = '\0';
xpru.xpru_errormsg = message;
xpru.xpru_updatemask = XPRU_ERRORMSG;
calla(xupdate, &xpru);
nmess = 0;
}
ST_Display_String(Item, S)
int Item;
char *S;
{
struct XPR_UPDATE xpru;
switch (Item) {
case STMsg:
xpru.xpru_msg = S;
xpru.xpru_updatemask = XPRU_MSG; break;
case STFile:
xpru.xpru_filename = S;
xpru.xpru_updatemask = XPRU_FILENAME; break;
case STUplSize:
xpru.xpru_filesize = callad(xfinfo, S, 1L);
xpru.xpru_updatemask = XPRU_FILESIZE; break;
default:
return;
}
calla(xupdate, &xpru);
}
ST_Display_CRC(Item)
int Item;
{
struct XPR_UPDATE xpru;
switch (Item) {
case 1:
xpru.xpru_blockcheck = "Check-6"; break;
case 2:
xpru.xpru_blockcheck = "Check-12"; break;
case 3:
xpru.xpru_blockcheck = "CRC-16"; break;
default:
return;
}
xpru.xpru_updatemask = XPRU_BLOCKCHECK;
calla(xupdate, &xpru);
}
extern char *p_pattern;
#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif
static int firsttime = TRUE;
int gnfile(fname, len)
char *fname;
int len; /* it is 50 */
{
char buffer[256];
static long gnstate;
if (firsttime) {
gnstate = callaa(xffirst, buffer, p_pattern);
if (gnstate) firsttime = FALSE;
} else {
gnstate = calldaa(xfnext, gnstate, buffer, p_pattern);
if (gnstate == 0L) firsttime = TRUE;
}
if (gnstate && (int)strlen(buffer) < len) {
strcpy(fname, buffer);
return((int)gnstate);
} else return(0);
}
void gninit(void) {
firsttime = TRUE;
}
char *EndPath(p)
char *p;
{
char *q;
q = p;
while (*p != '\0') {
if ((*p == '/') || (*p == ':'))
q = p + 1;
p++;
}
return(q);
}